﻿using System;
using System.Collections.Generic;
using System.IO;

namespace CSJ2K.Util.IO
{
    // This class can be refactored as extension methods simply by inserting 
    // the this keyword before Stream in the method prototypes if/when the 
    // necessary compiler attributes are included in System.dll instead of
    // System.Core.dll.
    internal static class StreamUtil
    {
        internal static byte ReadOctet(Stream s)
        {
            byte[] buffer = new byte[1];
            if (s.Read(buffer, 0, 1) < 1) throw (new EndOfStreamException());
            return buffer[0];
        }
        //
        // Summary:
        //     Reads count bytes from the current stream into a byte array and advances
        //     the current position by count bytes.
        //
        // Parameters:
        //   count:
        //     The number of bytes to read.
        //
        // Returns:
        //     A byte array containing data read from the underlying stream. This might
        //     be less than the number of bytes requested if the end of the stream is reached.
        //
        // Exceptions:
        //   System.IO.IOException:
        //     An I/O error occurs.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.ArgumentOutOfRangeException:
        //     count is negative.
        internal static byte[] ReadBytes(Stream s, int count)
        {
            byte[] buffer = new byte[count];
            int len = s.Read(buffer, 0, count);
            if (len < count) Array.Resize(ref buffer, len);
            return buffer;
        }
        internal static byte[] ReadExactBytes(Stream s, int count)
        {
            byte[] buffer = new byte[count];
            int len = s.Read(buffer, 0, count);
            if (len < count) throw(new EndOfStreamException("Expected " + count + " bytes but only read " + len + " bytes."));
            return buffer;
        }
        internal static byte[] ReadToEnd(Stream s)
        {
            int len;
            int position = 0;
            byte[] buffer = new byte[512];
            while ((len = s.Read(buffer, position, 512)) > 0)
            {
                position += len;
                if (len < 512)
                {
                    Array.Resize(ref buffer, position);
                    break;
                }
                Array.Resize(ref buffer, position + 512);
            }

            return buffer;
        }
            
        //
        // Summary:
        //     Reads a 2-byte signed integer from the current stream using big-endian
        //     encoding and advances the current position of the stream by two bytes.
        //
        // Returns:
        //     A 2-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static short ReadBInt16(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 2);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt16(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 4-byte signed integer from the current stream using big-endian
        //     encoding and advances the current position of the stream by four bytes.
        //
        // Returns:
        //     A 4-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static int ReadBInt32(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 4);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt32(buffer, 0));
        }
        //
        // Summary:
        //     Reads an 8-byte signed integer from the current stream using big-endian 
        //     encoding and advances the current position of the stream by eight bytes.
        //
        // Returns:
        //     An 8-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static long ReadBInt64(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 8);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt64(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 2-byte unsigned integer from the current stream using big-endian
        //     encoding and advances the position of the stream by two bytes.
        //
        // Returns:
        //     A 2-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static ushort ReadBUInt16(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 2);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt16(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 4-byte unsigned integer from the current stream using big-endian
        //     encoding and advances the position of the stream by four bytes.
        //
        // Returns:
        //     A 4-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static uint ReadBUInt32(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 4);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt32(buffer, 0));
        }
        //
        // Summary:
        //     Reads an 8-byte unsigned integer from the current stream using big-endian
        //     encoding and advances the position of the stream by eight bytes.
        //
        // Returns:
        //     An 8-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        internal static ulong ReadBUInt64(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 8);
            if (BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt64(buffer, 0));
        }


        //
        // Summary:
        //     Reads a 2-byte signed integer from the current stream using little-endian
        //     encoding and advances the current position of the stream by two bytes.
        //
        // Returns:
        //     A 2-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static short ReadInt16(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 2);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt16(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 4-byte signed integer from the current stream using little-endian
        //     encoding and advances the current position of the stream by four bytes.
        //
        // Returns:
        //     A 4-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static int ReadInt32(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 4);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt32(buffer, 0));
        }
        //
        // Summary:
        //     Reads an 8-byte signed integer from the current stream using little-endian 
        //     encoding and advances the current position of the stream by eight bytes.
        //
        // Returns:
        //     An 8-byte signed integer read from the current stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static long ReadInt64(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 8);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToInt64(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 2-byte unsigned integer from the current stream using little-endian
        //     encoding and advances the position of the stream by two bytes.
        //
        // Returns:
        //     A 2-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static ushort ReadUInt16(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 2);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt16(buffer, 0));
        }
        //
        // Summary:
        //     Reads a 4-byte unsigned integer from the current stream using little-endian
        //     encoding and advances the position of the stream by four bytes.
        //
        // Returns:
        //     A 4-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        internal static uint ReadUInt32(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 4);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt32(buffer, 0));
        }
        //
        // Summary:
        //     Reads an 8-byte unsigned integer from the current stream using little-endian
        //     encoding and advances the position of the stream by eight bytes.
        //
        // Returns:
        //     An 8-byte unsigned integer read from this stream.
        //
        // Exceptions:
        //   System.IO.EndOfStreamException:
        //     The end of the stream is reached.
        //
        //   System.IO.IOException:
        //     An I/O error occurs.
        //
        //   System.ObjectDisposedException:
        //     The stream is closed.
        internal static ulong ReadUInt64(Stream s)
        {
            byte[] buffer = ReadExactBytes(s, 8);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
            return (BitConverter.ToUInt64(buffer, 0));
        }
    }
}
